function ChatManager() {
	
    this._userId = "";
    this._userKey = "";
    this._updateKey = 0;

    this._currentChannelKey = "";
    this._channelInformation = [];
	
    this._chatConfiguration = {};

    this._history = [];
    this._historyIndex = 0;

    this._messageQueue = [];
    this._statusResponse = [];
    
    this._clientHasFocus = true;
    this._unreadEvents = 0;
    
    this._modulesAction = {}
    this._modulesData = []
	
    this._REGEX_COMMAND_BI = /^\/([A-z]*)[ ]*(.*)?/;
    this._REGEX_COMMAND_TRI = /([A-z0-9]*)[ ]*(.*)/;
	this._REGEX_WINDOW_TITLE = /(.*) (\([0-9]*\))?$/;
    /*
     * {} _initializeChat
     *
     * Initializes the PDCM chat manager.
     *
     * Parameters:
     * context    - {ChatManager} The PCDM chat manager class.     
     */	
    this._initializeChat = function (context) {
        $(document).ready( function() {
		
        	$(window).focus(function(){
        		context._clientHasFocus = true;
        		context._unreadEvents = 0;
        		context._handleUnreadEvents(context);
        	});
        	
        	$(window).blur(function(){
        		context._clientHasFocus = false;
        	});
        	
            // Hook the chat clearing handler
            $("#chatError").click(function(){
                $(this).hide();
            });
			
            $("#chatInput").keydown( function(e) {
                // Catch TAB
                if (e.keyCode == 0x09) {
                	$("#chatInput").val(
                			context._getMatchingUsername($("#chatInput").val(), context._getChannelData(context, "target_key", context._currentChannelKey, "target_users"))
                	);
                	return false;
                }
                // Catch ENTER
                else if(e.keyCode == 0x0d) {
                    context._processInput(context, $("#chatInput").val());
                    $("#chatInput").val('');
                }
                // Catch UP ARROW
                else if (e.keyCode == 0x26) {
                    context._historyIndex++;
                    context._handleHistoryFetch(context);
                }
                // Catch DOWN ARROW
                else if(e.keyCode == 0x28) {
                    context._historyIndex--;
                    context._handleHistoryFetch(context);
                }
            });

            // Hook the window resizing handler functionality
            $(window).resize( function() {
                $('.pcdm_chatContainer').height(
                    $(window).height() - 115
                    );
				
                $('.pcdm_userlistContainer').height(
                    $(window).height() - 90
                    );
				
                $('.pcdm_chatError').width(
                    $('.pcdm_chatContainer').width() - 40
                    );
            });
            $(window).resize();

            // Hook the status task's click function
            $(".pcdm_taskStatus").click(function(){
                context._handleTaskClick(context, "pcdm_paneStatus");
            });

            // Hook the help functionality
            $("#pcdm_help_nav").click(function(){
                context._actionHelp(context);
                return false;
            });
            
            // Attempt to reconnect where possible
//            $.ajax({
//                type: 'POST',
//                url: "action",
//                data: {
//                    action: "reconnect",
//                },
//                success: function(responseData, textStatus, xhr) {
//                    if(responseData["errors"].length > 0) {
//                    	context._loadUsernameModal(context);
//                    }
//                    else {
//                        context._userId = responseData["data"].user_id;
//                        context._userKey = responseData["data"].user_key;
//                        context._chatConfiguration = responseData["data"].configuration;
//    					
//                        $("#chatInput").focus();
//    					
//                        //context._handleUpdateTimer(context);
//                        //context._actionJoinChannel(context, context._chatConfiguration["default_channel"]);
//                    }
//                },
//	            error: function(xhr, textStatus, errorThrown) {
//	                context._handleChatError($("#chatError"), ["Unexpected authentication error."], 0);
//	            },
//	            dataType: "json"
//	        });
            
        	context._loadUsernameModal(context);
        });
    }

	this.insertActionModule = function(command, handler) {
		this._modulesAction[command] = handler;
	}

	this.insertDataModule = function(handler) {
		this._modulesData.push(handler);
	}
	
    /*
     *
     * Action Processors
     *
     */
	
    
    /*
     * {} _actionChangeUsername
     *
     * Attempts to change your current username.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.     
     * username	- {String} The new username.     
     */
    this._actionChangeUsername = function(context, username) {
        var hashVal = Math.random().toString().substr(2);
        $.ajax({
            type: 'POST',
            url: "action",
            data: {
                action: "nick",
                user_id: context._userId,
                username: username,
                message_key: hashVal,
                message_hash: hmac_sha256(context._userKey, hashVal, false)
            },
            success: function(responseData, textStatus, xhr) {
                if(responseData["errors"].length > 0) {
                    context._handleChatError($("#chatError"), responseData["errors"], 10000);
                }
                else {
                    context._actionGetEvents(context, [context._currentChannelKey]);
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                context._handleChatError($("#chatError"), ["Unable to change username to " + username], 10000);
            },
            dataType: "json"
        });
    }
    
    /*
     * {} _actionConnect
     *
     * Attempts to connect to the PCDM chat server with the supplied username.
     *
     * Parameters:
     * context    - {ChatManager} The PCDM chat manager class.     
     * username   - {String} The username the user wishes to use.     
     */	
    this._actionConnect = function(context, username) {
        $.ajax({
            type: 'POST',
            url: "action",
            data: {
                action: "connect",
                username: username,
            },
            success: function(responseData, textStatus, xhr) {
                if(responseData["errors"].length > 0) {
                    context._handleChatError($("#usernameError"), responseData["errors"], 0);
                }
                else {
                    context._userId = responseData["data"].user_id;
                    context._userKey = responseData["data"].user_key;
                    context._chatConfiguration = responseData["data"].configuration;
					
                    $.modal.close();
					
                    $("#chatInput").focus();
					
                    context._handleUpdateTimer(context);
                    context._actionJoinChannel(context, context._chatConfiguration["default_channel"]);
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                context._handleChatError($("#usernameError"), ["Unexpected communication error."], 0);
            },
            dataType: "json"
        });
    }
    
    /*
     * {} _actionGetEvents
     *
     * Grabs a new set of events from the PCDM server.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     * channel	- {Array<String>} The name of the channels for which new actions will be fetched. In none is specified all are fetched.
     */
    this._actionGetEvents = function(context, channels, updateKey) {
        var hashVal = Math.random().toString().substr(2);
        var startTime = new Date().getTime();

        $.ajax({
            type: 'POST',
            url: "action",
            data: {
                action: "get_events",
                targets: channels == undefined ? null : channels,
                update_key: updateKey == undefined ? context._updateKey : updateKey,
                user_id: context._userId,
                message_key: hashVal,
                message_hash: hmac_sha256(context._userKey, hashVal, false)
            },
            success: function(responseData, textStatus, xhr) {
                if(responseData["errors"].length > 0) {
                    context._handleChatError($("#chatError"), responseData["errors"], 10000);
                }
                else {
                    context._recalculateResponseTime(context, new Date().getTime() - startTime);
                    context._processActions(context, responseData["data"].actions);
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                context._handleChatError($("#chatError"), ["Unexpected communication error."], 10000);
            },
            dataType: "json"
        });
    }

    /*
     * {} _actionHelp
     *
     * Activates the help dialog.
     */
    this._actionHelp = function(context) {
        $.ajax({
            type: 'GET',
            url: "popup/help",
            success: function(responseData, textStatus, xhr) {
                $.modal(responseData, {
                    overlayClose: true
                });            	
            }
        });
    }
    
    /*
     * {} _actionJoinChannel
     *
     * Attempts to join a new PCDM channel.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.     
     * channel	- {String} The name of the channel the user wishes to join.     
     */	
    this._actionJoinChannel = function(context, channel) {
        var hashVal = Math.random().toString().substr(2);
        $.ajax({
            type: 'POST',
            url: "action",
            data: {
                action: "join",
                target: channel,
                user_id: context._userId,
                message_key: hashVal,
                message_hash: hmac_sha256(context._userKey, hashVal, false)
            },
            success: function(responseData, textStatus, xhr) {
                if(responseData["errors"].length > 0) {
                    context._handleChatError($("#chatError"), responseData["errors"], 10000);
                }
                else {
            		responseData["data"].type = 'channel';
                    context._channelInformation.push(responseData["data"]);
                    context._createNewWindow(context, responseData["data"].target_key, responseData["data"].target_name);
                    context._actionGetEvents(context, [responseData["data"].target_key], 0);

                    $("#task_"+responseData["data"].target_key).click();
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                context._handleChatError($("#chatError"), ["Unable to join requested channel " + channel], 10000);
            },
            dataType: "json"
        });
    }

    /*
     * {} _actionPartChannel
     *
     * Attempts to part the current PCDM channel.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     */
    this._actionPartChannel = function(context) {
        var hashVal = Math.random().toString().substr(2);

        if(context._currentChannelKey == null) {
            context._handleChatError($("#chatError"), ["Invalid channel."], 10000);
        }
        else {
            $.ajax({
                type: 'POST',
                url: "action",
                data: {
                    action: "part",
                    target: context._currentChannelKey,
                    user_id: context._userId,
                    message_key: hashVal,
                    message_hash: hmac_sha256(context._userKey, hashVal, false)
                },
                success: function(responseData, textStatus, xhr) {
                    if(responseData["errors"].length > 0) {
                        context._handleChatError($("#chatError"), responseData["errors"], 10000);
                    }
                    else {
                        $("#"+responseData["data"].target_key).remove();
                        $("#task_"+responseData["data"].target_key).remove();

                        for(var i in context._channelInformation) {
                            if(context._channelInformation[i].target_key == responseData["data"].target_key) {
                                context._channelInformation.splice(i, 1);
                            }
                        }

                        $(".pcdm_task").first().click();
                    }
                },
                error: function(xhr, textStatus, errorThrown) {
                    context._handleChatError($("#chatError"), ["Unable to part requested channel " + context._getChannelData(context, "target_key", context._currentChannelKey, "target_name")], 10000);
                },
                dataType: "json"
            });
        }
    }

    /*
     * {} _actionPrivateTalk
     *
     * Speaks a line to a channel.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     * channel	- {String} The name of the private channel the user wishes to speak to.
     * data	- {String} Data to speak to the private channel.
     */
    this._actionPrivateTalk = function(context, target, data) {

        var hashVal = Math.random().toString().substr(2);
        $.ajax({
            type: 'POST',
            url: "action",
            data: {
                action: "privatetalk",
                target: target,
                update_key: context._updateKey,
                data: data,
                user_id: context._userId,
                message_key: hashVal,
                message_hash: hmac_sha256(context._userKey, hashVal, false)
            },
            success: function(responseData, textStatus, xhr) {
                if(responseData["errors"].length > 0) {
                    context._handleChatError($("#chatError"), responseData["errors"], 10000);
                }
                else {
                	if(context._getChannelData(context, "target_key", responseData["data"].target_key, "target_key") == null) {
                		responseData["data"].type = 'private';
                        context._channelInformation.push(responseData["data"]);
                        context._createNewWindow(context, responseData["data"].target_key, responseData["data"].target_name);

                        $("#task_"+responseData["data"].target_key).click();
                	}
                	
                    context._actionGetEvents(context, [responseData["data"].target_key], 0);
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                context._handleChatError($("#chatError"), ["Unable to send information to requested private channel."], 10000);
            },
            dataType: "json"
        });
    }

    /*
     * {} _actionStatus
     *
     * Activates the status dialog.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     */
    this._actionStatus = function(context) {
        $.ajax({
            type: 'GET',
            url: "popup/status",
            success: function(responseData, textStatus, xhr) {
                $.modal(responseData, {
                    overlayClose: true,
                    onShow: function(dialog) {
                        $("#pcdm_statusUserId").html(context._userId);
                        $("#pcdm_statusUserKey").html(context._userKey);

                        var jqStatusHistory = $("#pcdm_statusHistory").empty();
                        for(var i in context._history) {
                            jqStatusHistory.prepend("<li>"+context._history[i]+"</li>");
                        }
                    }
                });            	
            }
        });
    }

    /*
     * {} _actionTalk
     *
     * Speaks a line to a channel.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.     
     * channel	- {String} The name of the channel the user wishes to talk to.
     * data	- {String} Data to speak to the channel.
     * selftalk	- {Boolean} Flag to set if a self talk is being executed.
     */	
    this._actionTalk = function(context, data, selftalk) {
 	
        if(selftalk == null) {
            selftalk = false;
        }

        if(context._currentChannelKey == null) {
            context._handleChatError($("#chatError"), ["Invalid channel."], 10000);
        }
        else {
            var hashVal = Math.random().toString().substr(2);
            $.ajax({
                type: 'POST',
                url: "action",
                data: {
                    action: selftalk ? "selftalk" : "talk",
                    target: context._currentChannelKey,
                    update_key: context._updateKey,
                    data: data,
                    user_id: context._userId,
                    message_key: hashVal,
                    message_hash: hmac_sha256(context._userKey, hashVal, false)
                },
                success: function(responseData, textStatus, xhr) {
                    if(responseData["errors"].length > 0) {
                        context._handleChatError($("#chatError"), responseData["errors"], 10000);
                    }
                    else {
                        context._actionGetEvents(context, [context._currentChannelKey]);
                    }
                    
                    if(context._messageQueue.length > 0){
                    	context._actionTalk(context, context._messageQueue.shift());                    	
                    }
                },
                error: function(xhr, textStatus, errorThrown) {
                    context._handleChatError($("#chatError"), ["Unable to send information to requested channel."], 10000);
                    
                    if(context._messageQueue.length > 0){
                    	context._actionTalk(context, context._messageQueue.shift());                    	
                    }
                },
                dataType: "json"
            });
        }
    }
	
    /*
     * {} _processActions
     *
     * Processes a list of actions to be displayed to the user.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.     
     * actions	- {Action} List of actions which need to be displayed.
     */	
    this._processActions = function(context, actions) {
    	
    	
        if(actions.length > 0) {
        	
        	actions.sort(function(a, b) {return b.id - a.id;});
        	
            var jqStatusPane = $("#pcdm_paneStatus");
            for(var i=actions.length-1; i >= 0; i--) {
            	
                if((actions[i].id == undefined) || (actions[i].id > context._updateKey)) {

	                var jqChatContainer = $("#"+actions[i].target);
	
	                if(actions[i].action == "JOIN") {
	                    jqChatContainer.append("<div class='actionJoin'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> has joined.</div>");
	                    jqStatusPane.append("<div class='actionJoin'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> has joined "+context._getChannelData(context, "target_key", actions[i].target, "target_name")+".</div>");
	
	                    var channel = context._getChannelData(context, "target_key", actions[i].target, null);
	                    if(channel.target_users != undefined && channel.target_users[actions[i].username] == undefined) {
	                    	channel.target_users[actions[i].username] = actions[i].username_id;
	                    }
	                    
	                    if(actions[i].target == context._currentChannelKey) {
	                        context._handleUserlistRefresh(context, actions[i].target);
	                    }
	                }
	                else if(actions[i].action == "PART") {
	                    jqChatContainer.append("<div class='actionPart'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> has parted.</div>");
	                    jqStatusPane.append("<div class='actionPart'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> has parted "+context._getChannelData(context, "target_key", actions[i].target, "target_name")+".</div>");
	
	                    var channel = context._getChannelData(context, "target_key", actions[i].target, null);
	                    if(channel.target_users != undefined && channel.target_users[actions[i].username] != undefined) {
	                        delete channel.target_users[actions[i].username];
	                    }
	
	                    if(actions[i].target == context._currentChannelKey) {
	                        context._handleUserlistRefresh(context, actions[i].target);
	                    }
	                }
	                else if(actions[i].action == "DISCONNECT") {
	                    jqChatContainer.append("<div class='actionDisconnect'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> has disconnected.</div>");
	                    jqStatusPane.append("<div class='actionDisconnect'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> has disconnected "+context._getChannelData(context, "target_key", actions[i].target, "target_name")+".</div>");
	
	                    var channel = context._getChannelData(context, "target_key", actions[i].target, null);
	                    if(channel.target_users != undefined && channel.target_users[actions[i].username] != undefined) {
	                        delete channel.target_users[actions[i].username];
	                    }
	
	                    if(actions[i].target == context._currentChannelKey) {
	                        context._handleUserlistRefresh(context, actions[i].target);
	                    }
	                }
	                else if(actions[i].action == "TALK") {
	                	var data = context._processDataModules(context, actions[i].data);
	                    jqChatContainer.append("<div class='actionTalk'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername' style='color: #"+context._encodeUsername(actions[i].username)+"'>"+actions[i].username+":</span> "+data+" </div>");
	                    jqStatusPane.append("<div class='actionTalk'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span>["+context._getChannelData(context, "target_key", actions[i].target, "target_name")+"]</span> <span class='actionUsername'>"+actions[i].username+"</span>: "+data+" </div>");
	                }
	                else if(actions[i].action == "PRIVATETALK") {
	                	var data = context._processDataModules(context, actions[i].data);
	                    if(context._getChannelData(context, "target_key", actions[i].target, "target_key") == null) {
	                        context._channelInformation.push({"target_key" : actions[i].target, "target_name" : actions[i].username});
	                        context._createNewWindow(context, actions[i].target, actions[i].username);
	                        jqChatContainer = $("#"+actions[i].target);
	                    }
	
	                    jqChatContainer.append("<div class='actionTalk'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername' style='color: #"+context._encodeUsername(actions[i].username)+"'>"+actions[i].username+":</span> "+data+" </div>");
	                    jqStatusPane.append("<div class='actionTalk'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span>[MSG]</span> <span class='actionUsername'>"+actions[i].username+"</span>: "+data+" </div>");
	                }
	                else if(actions[i].action == "SELFTALK") {
	                	var data = context._processDataModules(context, actions[i].data);
	                    jqChatContainer.append("<div class='actionSelfTalk'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionUsername'>"+actions[i].username+"</span> "+data+"</div>");
	                    jqStatusPane.append("<div class='actionSelfTalk'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span>["+context._getChannelData(context, "target_key", actions[i].target, "target_name")+"]</span> <span class='actionUsername'>"+actions[i].username+"</span> "+data+" </div>");
	                }
	                else if(actions[i].action == "NICK") {
	                    jqChatContainer.append("<div class='actionNick'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionNick'>"+actions[i].data+"</span> has changed their name to <span class='actionNick'>"+actions[i].username+"</span>.</div>");
	                    jqStatusPane.append("<div class='actionNick'><span class='actionTimestamp'>"+context._formatTimestamp(actions[i].timestamp)+"</span><span class='actionNick'>"+actions[i].data+"</span> has changed their name to <span class='actionNick'>"+actions[i].username+"</span>.</div>");

	                    var channel = context._getChannelData(context, "target_key", actions[i].target, null);
	                    
	                    if(channel.target_users != undefined && channel.target_users[actions[i].data] != undefined) {
	                    	channel.target_users[actions[i].username] = channel.target_users[actions[i].data];
	                        delete channel.target_users[actions[i].data];
	                        
	                        if(channel.type == 'private') {
	                        	$("#task_"+actions[i].target).html(actions[i].username);
	                        }
	                    }

	                    if(actions[i].target == context._currentChannelKey) {
	                        context._handleUserlistRefresh(context, actions[i].target);
	                    }
	                }
	                
	                if(actions[i].target != context._currentChannelKey) {
	                    $("#task_"+actions[i].target).addClass("pcdm_taskUnread");
	                }
	                
	                if(!context._clientHasFocus) {
	                	context._unreadEvents++;
	                	context._handleUnreadEvents(context);
	                }
	                context._updateKey = actions[i].id != undefined ? actions[i].id : context._updateKey;
            	}
            }

            jqChatContainer.attr({scrollTop: jqChatContainer.attr("scrollHeight") - jqChatContainer.height()});
            jqStatusPane.attr({scrollTop: jqStatusPane.attr("scrollHeight") - jqStatusPane.height()});
        }
    }

    this._processDataModules = function(context, data) {
    	for(var handler in context._modulesData) {
    		data = context._modulesData[handler](context, data);
    	}
    	
    	return data;
    }
    
    /*
     * {} _processInput
     *
     * Processes the input to determine if further processing is necessary.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.     
     * input	- {String} Input to be processed.
     */
    this._processInput = function(context, input) {
        input = $.trim(input);
        context._history.push(input);
        context._historyIndex = 0;

        var matches = input.match(context._REGEX_COMMAND_BI);

        if(matches != null) {
            context._processCommand(context, matches[1], matches[2]);
        }
        else {
        	context._messageQueue.push(input);
        	
        	if(context._messageQueue.length <= 1) {
                context._actionTalk(context, context._messageQueue.shift());
        	}
        }
    }
	
    /*
     * {} _processCommand
     *
     * Processes the inputed command to determine if further processing is necessary.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.     
     * command	- {String} Command to be processed.
     * data	- {String} Additional command parameters to be processed.
     */
    this._processCommand= function(context, command, data) {
        command = command.toLowerCase();
        
        if(command == 'help') {
            context._actionHelp(context);
        }
        else if(command == 'join') {
            context._actionJoinChannel(context, data);
        }
        else if(command == 'me') {
            context._actionTalk(context, data, true);
        }
        else if(command == 'nick') {
            context._actionChangeUsername(context, data);
        }
        else if(command == 'msg') {
            var matches = data.match(context._REGEX_COMMAND_TRI);
            if(matches != null) {
                context._actionPrivateTalk(context, matches[1], matches[2]);
            }
        }
        else if(command == 'part') {
            context._actionPartChannel(context, data);
        }
        else if(command == 'say') {
            context._actionTalk(context, data);
        }
        else if(command == 'status') {
            context._actionStatus(context);
        }
        else if(context._modulesAction.hasOwnProperty(command)) {
        	context._modulesAction[command](context, data);
        }
        else {
            context._handleChatError($("#chatError"), ["Unknown command "+command+"."], 10000);
        }
    }

    /*
     *
     * Event Handlers
     *
     */

    /*
     * {} _handleChatError
     *
     * Displays a list of error messages to the user.
     *
     * Parameters:
     * jqError    	- {jQuery} A jQuery container whose contents will display the error messages.
     * errorMessages    - {String[]} A list of error messages to display to the user.
     * timeout		- {Integer} A timeout in ms before the messages are automatically removed.
     */
    this._handleChatError = function(jqError, errorMessages, timeout) {
        var errorMessage = "";
        var errorTimestamp = new Date().toLocaleTimeString();
        var jqStatusPane = $("#pcdm_paneStatus");
        for(var i=0; i <  errorMessages.length; i++) {
            errorMessage += "<li>" + errorMessages[i] + "</li>";
            jqStatusPane.append("<div class='actionError'><span class='actionTimestamp'>"+errorTimestamp+"</span>"+errorMessages[i]+"</div>");
        }
        
        jqStatusPane.attr({scrollTop: jqStatusPane.attr("scrollHeight") - jqStatusPane.height()});

        jqError
        .html("<ul><li>"+errorMessage+"</li></ul>")
        .fadeIn();


        if(timeout > 0) {
            setTimeout(function(){
                jqError.fadeOut();
            }, timeout);
        }
    }

    /*
     * {} _handleHistoryFetch
     *
     * Handles the history request of the client.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     */
    this._handleHistoryFetch = function(context) {
        if(context._historyIndex <= 0) {
            context._historyIndex = 0;
            $("#chatInput").val("");
        }
        else if(context._historyIndex > context._history.length) {
            context._historyIndex = context._history.length
        }
        else {
            $("#chatInput").val(context._history[context._history.length - context._historyIndex]);
        }
    }

    /*
     * {} _handleTaskClick
     *
     * Handles the event of a user clicking on a task tab.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     * target   - {String} The channel key indicating which channel was selected.
     */
    this._handleTaskClick = function(context, target) {
        $(".pcdm_task").removeClass("pcdm_taskCurrent");
        $("#task_"+target).addClass("pcdm_taskCurrent").removeClass("pcdm_taskUnread");

        $(".pcdm_chatContainer").hide();
        var jqChatContainer = $("#"+target);
        jqChatContainer
            .show()
            .attr({scrollTop: jqChatContainer.attr("scrollHeight") - jqChatContainer.height()});
        context._handleUserlistRefresh(context, target);
        if(target != "pcdm_paneStatus") {
            context._currentChannelKey = target;
        }
        else {
            context._currentChannelKey = null;
        }
    }
    
    /*
     * {} _handleUnreadEvents
     *
     * Handles unread event when the chat window does not have focus.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     */
    this._handleUnreadEvents = function(context) {
    	var matches = document.title.match(context._REGEX_WINDOW_TITLE);
    	var windowTitle =  matches == null ? document.title : matches[1];
    	document.title = context._unreadEvents > 0 ? windowTitle + " ("+context._unreadEvents+")" : windowTitle;
    }
    
    /*
     * {} _handleUpdateTimer
     *
     * Periodic update function used mainly to poll for new events.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     */
    this._handleUpdateTimer = function(context) {
        context._actionGetEvents(context);

        setTimeout(
            function() {
                context._handleUpdateTimer(context);
            },
            context._chatConfiguration["update_timeout"]
        );
    }

    /*
     * {} _handleUserlistRefresh
     *
     * Handles the need to refresh the user list for a specific channel.
     *
     * Parameters:
     * context	- {ChatManager} The PCDM chat manager class.
     * target   - {String} The channel key indicating which channel was selected.
     */
    this._handleUserlistRefresh = function(context, target) {
        var jqUserContainer = $(".pcdm_userlistContainer");
        jqUserContainer.empty();

        for(var i in context._channelInformation) {
            if(context._channelInformation[i].target_key == target) {
                var userlist = [];
                for(var currUsername in context._channelInformation[i].target_users) {
                    userlist.push(currUsername);
                }
                userlist.sort();

                for(var username in userlist) {
                    jqUserContainer.append("<div class='channelUsername'><span style='color: #"+context._encodeUsername(userlist[username])+"'>"+userlist[username]+"</span></div>");
                }

                return;
            }
        }

    }
    
    /*
     *
     * Utility Functions
     * 
     */

    /*
     * {} _createNewWindow
     * 
     * Creates a new task window for the chat client.
     * 
     * Parameters:
     * context      - {ChatManager} The PCDM chat manager class.
     * target_key	- {String} The target identification hash for the newly created window
     * target_name	- {String} the target name for the newly created window
     */
    this._createNewWindow = function(context, target_key, target_name) {
        $("#pcdm_paneStatus").before("<div class='pcdm_chatContainer pcdm_container' id='"+target_key+"'></div>")
        $("#pcdm_taskBar").append("<div class='pcdm_task' id='task_"+target_key+"'>"+target_name+"</div>")

        $("#task_"+target_key).click(function(){
            context._handleTaskClick(context, target_key);
        });

        $(window).resize();
    }

    /*
     * {} _encodeUsername
     *
     * Encodes a username into a known value representing a hex color.
     *
     * Parameters:
     * username	- {String} Username to be encoded.
     */
    this._encodeUsername = function(username) {
        var val = 0;
	var i=0;

        for(i=0; i < username.length; i++) {
            val += username.charCodeAt(i);
        }

        var retVal = (0xffffff * Math.abs(Math.sin(val))).toString(16).split('.')[0];

        for(i=retVal.length; i < 6; i++) {
            retVal = "0" + retVal;
        }

        return retVal;
    }

    /*
     * {} _formatTimestamp
     * 
     * Formats a second-based epoch timestamp into a human readable format.
     * 
     * Parameters:
     * timestamp	- {Integer} Second-based epoch timestamp
     */
    this._formatTimestamp = function(timestamp) {
        var timestampDate = new Date(timestamp * 1000);

        return timestampDate.toLocaleTimeString();
    }

    /*
     * {} _getChannelData
     *
     * Gets the requested channel key from the stored map.
     *
     * Parameters:
     * context      - {ChatManager} The PCDM chat manager class.
     * search_key   - {String} Key upon which to perform the sequential search.
     * search_value - {String} Value upon which to match the key's value.
     * data_key     - {String} Key upon which to return the data. If null the channel object is returned.
     */	
    this._getChannelData = function(context, search_key, search_value, data_key) {
		
        for(var i in context._channelInformation) {
            if(context._channelInformation[i][search_key] == search_value) {
            	if(data_key == null) {
            		return context._channelInformation[i];
            	}
            	else {
                    return context._channelInformation[i][data_key];
            	}
            }
        }
		
        return null;
    }
    
    /*
     * {} _getMatchingUsername
     *
     * Gets the closest unique matching username from the supplied userlist.
     *
     * Parameters:
     * text      	- {String} Input line to be parsed and matched.
     * target_users	- {String[]} User list of the target which shall be matched.
     */	
    this._getMatchingUsername = function(text, target_users) {
    	var tokens = text.split(" ");
    	var searchToken = tokens[tokens.length - 1];
    	
    	if(searchToken.length == 0) {
    		return text;
    	}
    	
    	var matchingUsers = [];
    	for(var user in target_users) {
    		if(user.indexOf(searchToken) == 0) {
    			matchingUsers.push(user);
    		}
    	}
    	
    	if(matchingUsers.length != 1) {
    		return text
    	}
    	else {
    		tokens[tokens.length - 1] = matchingUsers[0] + ": ";
    	}
    	
    	return tokens.join(" ");
    }
    
    this._loadUsernameModal = function(context) {
	    // Instantiate the username modal box
	    $.ajax({
	        type: 'GET',
	        url: "popup/login",
	        success: function(responseData, textStatus, xhr) {
	            $.modal(responseData, {
	                escClose: false,
	                onShow: function() {
	                    $("#chatUsername").keypress( function(e) {
	                        if(e.keyCode == 13){
	                            $("#usernameError").hide();
	                            context._actionConnect(context, $("#chatUsername").val());
	                        }
	                    });
	                }
	            });
	        	
	        }
	    });
    }
    
    /*
     * {} _recalculateResponseTime
     *
     * Recalculates the response time of the server over the last 10 requests.
     *
     * Parameters:
     * context      - {ChatManager} The PCDM chat manager class.
     * responseTime - {Integer} The last detected response time by the server.
     */
    this._recalculateResponseTime = function(context, responseTime){
        if(context._statusResponse.push(responseTime) > 10) {
            context._statusResponse.shift();
        }

        var total = 0;
        var i = 0
        for(i in context._statusResponse) {
            total += context._statusResponse[i];
        }

        if(i > 0) {
            $("#pcdm_statusResponse").html((total / i).toFixed(2))
        }
    }

    /* Constructor */
    {
        this._initializeChat(this);
    }
}